﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using CSharpExperiments.Metaprogramming;

namespace CSharpExperiments.Collections
{
	public static class Map
	{
		/// <summary>Creates a Dictionary from a list of simple lambda expressions.</summary>
		[Obsolete("This is known to be a bad idea - it is not portable between languages and abuses the lambda syntax!")]
		public static Dictionary<String, T> Create<T>(params Expression<Func<Object, T>>[] lambdas)
		{
			return lambdas.ToDictionary(
				k => k.Parameters.First().Name,
				v => v.Compile().Invoke(null));
		}

		/// <summary>Creates a Dictionary from an anonymous object.</summary>
		/// <remarks>Usage: var d = Dict.Create(new { key1 = "value1", key2 = 5 });</remarks>
		public static Dictionary<String, Object> Create(Object obj)
		{
			if (obj == null)
				return new Dictionary<String, Object>();

			return obj.Props()
				.Where(x => x.GetIndexParameters().Length == 0)
				.ToDictionary(k => k.Name, v => v.GetValue(obj, null));
		}

		/// <summary>Creates a Dictionary from key/value pairs. The 1st, 3rd, 5th, etc. elements are keys;
		/// the 2nd, 4th, 6th, etc. elements are values mapped from the preceeding key.</summary>
		/// <remarks>An odd number of args will cause the last arg to be a key with a null value.</remarks>
		public static IDictionary<A, B> Create<A, B>(params Object[] pairs)
		{
			IDictionary<A, B> dict = new Dictionary<A, B>();

			for (int i = 0; i < pairs.Length; i += 2)
			{
				if (i == pairs.Length - 1)
				{
					dict = dict.WithDefault((B) pairs[i]);
				}
				else
				{
					var key = (A) pairs[i];
					var val = (B) pairs[i + 1];
					dict.Add(key, val);
				}
			}

			return dict;
		}

		public static IDictionary<String, Object> Create(params Object[] pairs)
		{
			return Create<String, Object>(pairs);
		}

		public static Dictionary<A, B> ToDictionary<A, B>(this IEnumerable<Tuple<A, B>> seq)
		{
			return seq.ToDictionary(x => x.Item1, x => x.Item2);
		}

		public static IDictionary<A, B> WithDefault<A, B>(this IDictionary<A, B> dict, B defaultValue)
		{
			return dict.WithDefault(_ => defaultValue);
		}

		public static IDictionary<A, B> WithDefault<A, B>(this IDictionary<A, B> dict, Func<A, B> valueFactory)
		{
			return new DefaultableDictionary<A, B>(dict, valueFactory);
		}

		public static Dictionary<int, T> IntoDictionary<T>(this List<T> list)
		{
			return list.Select((item, index) => Tuple.Create(index, item)).ToDictionary();
		}

		public static List<T> IntoList<T>(this Dictionary<int, T> dict)
		{
			var list = new List<T>(Math.Max(0, dict.Keys.Max() - 1));

			for (int i = 0; i < list.Count; ++i)
				list[i] = default(T);

			foreach (var keyValuePair in dict)
				if (keyValuePair.Key >= 0)
					list[keyValuePair.Key] = keyValuePair.Value;

			return list;
		}
	}
}
